import AliOSS from 'ali-oss';
import RawaOSS from '../../tools/rawa-oss';
import { Vue, Component, Prop } from 'vue-property-decorator';
import axios from 'axios';
// import { defaultSettings } from '@/settings'

// type OSS = AliOSS | RawaOSS
class Reducer {
    constructor(cb, fn, ...args) {
        this.cb = cb;
        this.fn = fn;
        this.args = args;
    }

    // cb = (data) => void

    // fn = (...args) => Promise
    // fn(...args) {

    // }

    args = []; // eslint-disable-line

    loading = false;

    queue = [];

    trigger() {
        if (this.loading) {
            return new Promise((resolve, reject) => {
                // this.queue.push({ resolve, reject });
                this.queue.unshift({ resolve, reject });
            });
        } else {
            this.loading = true;
            return this.fn(...this.args)
                .then((data) => {
                    if (this.cb) this.cb(data);
                    this.queue.forEach((v) => {
                        v.resolve(data);
                    });
                    return data;
                })
                .catch((e) => {
                    this.queue.forEach((v) => {
                        v.reject(e);
                    });
                    return Promise.reject(e);
                })
                .finally(() => {
                    this.queue = [];
                    this.loading = false;
                });
        }
    }
}

// export interface OssConfig {
//   accessKeyId: string;

//   accessKeySecret: string;

//   securityToken: string;

//   expiration: string;

//   requestId: string;

//   bucket: string;

//   endpoint: string;

// }

// export interface UploadParams {
//   name: string;
//   file: File;
//   path: string;
//   actualPath: string;
//   parentId: number;
//   beforeUpload: (params: UploadParams, queue[]) => Promise<any>;
//   afterUpload: (params: UploadParams, resp: UploadResp, queue[]) => Promise<any>;
// }

// export interface UploadResp {
//   name: string;
//   url: string;
//   data: object;
//   message?: string;
// }

export class FileData {
    constructor(params) {
        this.params = params;
    }

    // params: UploadParams;

    resp = null;

    progress = 0;

    // status: 'waiting' | 'uploading' | 'success' | 'error' | 'cancel' | 'abort' = 'waiting';
    status = 'waiting';

    client = null;

    checkpoint = null;

    dataSource = 'rawa';
}

// export type OssDataSource = 'rawa' | 'aliyun'
// export interface OssOptions {
//   loadConfigFn: (dataSource: OssDataSource) => Promise<OssConfig>;
// }

let uid = 1;

@Component
export default class Oss extends Vue {
    @Prop() options;
    options = {};
    queue = [];

    maxUploading = 5;

    configMap = {};

    tokenReducerMap = {
        rawa: new Reducer(
            (data) => {
                this.configMap.rawa = data;
            },
            this.options.loadConfigFn,
            'rawa'
        ),
        aliyun: new Reducer(
            (data) => {
                this.configMap.aliyun = data;
            },
            this.options.loadConfigFn,
            'aliyun'
        )
    };

    // 文件管理器显示/隐藏
    visible = false;

    // 令牌已过期
    tokenHasExpired(dataSource) {
        const config = this.configMap[dataSource];
        if (!config) return true;
        if (dataSource === 'rawa') return false;
        const expiration = new Date(config.expiration);
        return Date.now() - expiration.getTime() > 60 * 1000;
    }

    loadConfig(dataSource) {
        const fn = this.options?.loadConfigFn;
        if (!fn) throw new Error('未配置OSS loadConfigFn');
        if (this.tokenHasExpired(dataSource)) {
            // 处理fn是undefined问题 进行重新赋值
            this.tokenReducerMap[dataSource].fn ??= fn;
            return this.tokenReducerMap[dataSource].trigger();
        } else {
            return Promise.resolve(this.configMap[dataSource]);
        }
    }

    // 获取上下文
    getContext(dataSource) {
        return this.loadConfig(dataSource).then((config) => {
            const Inst = dataSource === 'rawa' ? RawaOSS : AliOSS;
            const client = new Inst({
                endpoint: config?.endpoint,
                accessKeyId: config?.accessKeyId,
                accessKeySecret: config?.accessKeySecret,
                stsToken: config?.securityToken,
                bucket: config?.bucket
            });
            return { client, config };
        });
    }

    // 计划上载
    scheduleUpload() {
        const handler = setInterval(() => {
            const num = this.queue?.filter((v) => v.status === 'uploading').length;
            this.queue
                ?.filter((v) => v.status === 'waiting')
                .some((v, i) => {
                    v.status = 'uploading';
                    this.getContext(v.dataSource)
                        .then(({ client, config }) => {
                            const path = config.data?.requestId + `-${uid++}/` + encodeURIComponent(v.params.name);
                            v.client = client;
                            client
                                .multipartUpload(
                                    path,
                                    v.params.file,
                                    {
                                        progress: function (p, checkpoint) {
                                            v.progress = Number(Number(p * 100).toFixed(1));
                                            v.checkpoint = checkpoint;
                                        },
                                        checkpoint: v.checkpoint || undefined
                                    },
                                    v.params?.moduleType,
                                    this
                                )
                                .then((resp) => {
                                    //放到分片上传的最后一个接口进行处理
                                    const data = {
                                        name: resp.name,
                                        url: resp.res?.requestUrls[0],
                                        data: resp
                                    };
                                    //执行最后的上传接口
                                    v.progress = 100;
                                    v.status = 'success';
                                    v.resp = data;
                                    v.isShowStatus = true;
                                    return v.params.afterUpload(v.params, data, this.queue).then(() => {
                                        console.log('v.params.afterUpload', data, this.queue);
                                    });
                                    // return v.params.afterUpload(v.params, data, this.queue).then(() => {
                                    //     v.progress = 100;
                                    //     v.status = 'success';
                                    //     v.resp = data;
                                    // });
                                })
                                .catch((e) => {
                                    console.error(1111111111, e);
                                    if (client.isCancel()) {
                                        if (v.status !== 'abort') {
                                            v.status = 'cancel';
                                        }
                                    } else if (e.name === 'abort') {
                                        v.status = 'abort';
                                        v.progress = 0;
                                        v.checkpoint = null;
                                    } else {
                                        v.status = 'error';
                                        v.resp = { name: path, url: '', message: e.message || e?.data?.message, data: e };
                                        console.error(e);
                                    }
                                });
                        })
                        .catch((e) => {
                            console.log('this.getContext----------报错', e);
                            v.status = 'waiting';
                        });
                });
        }, 1000);
        return () => {
            clearInterval(handler);
        };
    }

    upload(params) {
        params.beforeUpload(params, this.queue).then(() => {
            const item = new FileData(params);
            // this.queue.push(item);
            this.queue.unshift(item);
            this.visible = true;
        });
    }

    signatureUrl(url, response) {
        // const dataSource: OssDataSource = new URL(url).hostname.indexOf('aliyun') !== -1 ? 'aliyun' : 'rawa'
        const dataSource = 'rawa';
        return this.getContext(dataSource).then(({ client, config }) => {
            const name = new URL(url).pathname.slice(1);
            const baseUrl = axios.defaults.baseURL;

            return `${baseUrl}/${name}`;
        });
    }

    cancel(fileData) {
        if (!fileData.client || fileData.status !== 'uploading' || !fileData.checkpoint) throw new Error('该状态不能暂停上传');
        fileData.client.cancel();
    }

    abort(fileData) {
        if (['waiting'].includes(fileData.status)) {
            fileData.status = 'abort';
            return;
        }
        if (!fileData.client || !['uploading', 'cancel'].includes(fileData.status) || !fileData.checkpoint) throw new Error('该状态不能终止上传');
        fileData.client.abortMultipartUpload(fileData.checkpoint?.name, fileData.checkpoint?.uploadId);
        fileData.status = 'abort';
        fileData.checkpoint = null;
        fileData.progress = 0;
    }

    resume(fileData) {
        fileData.status = 'waiting';
    }

    created() {
        const fn = this.scheduleUpload();
        this.$once('hook:beforeDestroy', function () {
            fn();
        });
    }
}
